package com.thz.system.utils.redis;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Redis工具类
 * 封装常用的Redis操作
 * 
 * @author THZ
 * @date 2025
 */
@Slf4j
@Component
public class RedisUtils {
    
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    @Autowired
    private ObjectMapper objectMapper;
    
    // =============================String操作=============================
    
    /**
     * 设置字符串值
     * 
     * @param key 键
     * @param value 值
     */
    public void set(String key, String value) {
        try {
            stringRedisTemplate.opsForValue().set(key, value);
        } catch (Exception e) {
            log.error("Redis设置值失败: key={}", key, e);
        }
    }
    
    /**
     * 设置字符串值并指定过期时间
     * 
     * @param key 键
     * @param value 值
     * @param timeout 过期时间
     * @param unit 时间单位
     */
    public void set(String key, String value, long timeout, TimeUnit unit) {
        try {
            stringRedisTemplate.opsForValue().set(key, value, timeout, unit);
        } catch (Exception e) {
            log.error("Redis设置值失败: key={}, timeout={}, unit={}", key, timeout, unit, e);
        }
    }
    
    /**
     * 设置对象值（自动序列化为JSON）
     * 
     * @param key 键
     * @param value 值
     */
    public void setObject(String key, Object value) {
        try {
            String jsonValue = objectMapper.writeValueAsString(value);
            stringRedisTemplate.opsForValue().set(key, jsonValue);
        } catch (JsonProcessingException e) {
            log.error("Redis设置对象失败: key={}", key, e);
        }
    }
    
    /**
     * 设置对象值并指定过期时间
     * 
     * @param key 键
     * @param value 值
     * @param timeout 过期时间
     * @param unit 时间单位
     */
    public void setObject(String key, Object value, long timeout, TimeUnit unit) {
        try {
            String jsonValue = objectMapper.writeValueAsString(value);
            stringRedisTemplate.opsForValue().set(key, jsonValue, timeout, unit);
            log.info("✅ Redis设置对象成功: key={}, ttl={}秒", key, timeout);
        } catch (JsonProcessingException e) {
            log.error("❌ Redis JSON序列化失败: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis设置对象失败（序列化错误）", e);
        } catch (Exception e) {
            log.error("❌ Redis连接或操作失败: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis设置对象失败（连接错误）", e);
        }
    }
    
    /**
     * 获取字符串值
     * 
     * @param key 键
     * @return 值
     */
    public String get(String key) {
        try {
            return stringRedisTemplate.opsForValue().get(key);
        } catch (Exception e) {
            log.error("Redis获取值失败: key={}", key, e);
            return null;
        }
    }
    
    /**
     * 获取对象值（自动反序列化）
     * 
     * @param key 键
     * @param clazz 目标类型
     * @return 对象
     */
    public <T> T getObject(String key, Class<T> clazz) {
        try {
            String jsonValue = stringRedisTemplate.opsForValue().get(key);
            if (jsonValue == null) {
                return null;
            }
            return objectMapper.readValue(jsonValue, clazz);
        } catch (JsonProcessingException e) {
            log.error("Redis获取对象失败: key={}", key, e);
            return null;
        }
    }
    
    /**
     * 删除键
     * 
     * @param key 键
     * @return 是否成功
     */
    public Boolean delete(String key) {
        try {
            return stringRedisTemplate.delete(key);
        } catch (Exception e) {
            log.error("Redis删除键失败: key={}", key, e);
            return false;
        }
    }
    
    /**
     * 批量删除键
     * 
     * @param keys 键集合
     * @return 删除的数量
     */
    public Long delete(Collection<String> keys) {
        try {
            return stringRedisTemplate.delete(keys);
        } catch (Exception e) {
            log.error("Redis批量删除键失败: keys={}", keys, e);
            return 0L;
        }
    }
    
    /**
     * 判断键是否存在
     * 
     * @param key 键
     * @return 是否存在
     */
    public Boolean exists(String key) {
        try {
            return stringRedisTemplate.hasKey(key);
        } catch (Exception e) {
            log.error("Redis判断键存在失败: key={}", key, e);
            return false;
        }
    }
    
    /**
     * 设置过期时间
     * 
     * @param key 键
     * @param timeout 过期时间
     * @param unit 时间单位
     * @return 是否成功
     */
    public Boolean expire(String key, long timeout, TimeUnit unit) {
        try {
            return stringRedisTemplate.expire(key, timeout, unit);
        } catch (Exception e) {
            log.error("Redis设置过期时间失败: key={}, timeout={}, unit={}", key, timeout, unit, e);
            return false;
        }
    }
    
    /**
     * 获取剩余过期时间
     * 
     * @param key 键
     * @param unit 时间单位
     * @return 剩余时间
     */
    public Long getExpire(String key, TimeUnit unit) {
        try {
            return stringRedisTemplate.getExpire(key, unit);
        } catch (Exception e) {
            log.error("Redis获取过期时间失败: key={}, unit={}", key, unit, e);
            return null;
        }
    }
    
    /**
     * 获取剩余过期时间（秒）
     * 
     * @param key 键
     * @return 剩余时间（秒）
     */
    public Long getExpire(String key) {
        return getExpire(key, TimeUnit.SECONDS);
    }
    
    // =============================Hash操作=============================
    
    /**
     * Hash设置值
     * 
     * @param key 键
     * @param field 字段
     * @param value 值
     */
    public void hSet(String key, String field, String value) {
        try {
            stringRedisTemplate.opsForHash().put(key, field, value);
        } catch (Exception e) {
            log.error("Redis Hash设置值失败: key={}, field={}", key, field, e);
        }
    }
    
    /**
     * Hash获取值
     * 
     * @param key 键
     * @param field 字段
     * @return 值
     */
    public String hGet(String key, String field) {
        try {
            Object value = stringRedisTemplate.opsForHash().get(key, field);
            return value != null ? value.toString() : null;
        } catch (Exception e) {
            log.error("Redis Hash获取值失败: key={}, field={}", key, field, e);
            return null;
        }
    }
    
    /**
     * Hash获取所有字段和值
     * 
     * @param key 键
     * @return Map
     */
    public Map<Object, Object> hGetAll(String key) {
        try {
            return stringRedisTemplate.opsForHash().entries(key);
        } catch (Exception e) {
            log.error("Redis Hash获取所有值失败: key={}", key, e);
            return null;
        }
    }
    
    /**
     * Hash删除字段
     * 
     * @param key 键
     * @param fields 字段
     * @return 删除的数量
     */
    public Long hDelete(String key, Object... fields) {
        try {
            return stringRedisTemplate.opsForHash().delete(key, fields);
        } catch (Exception e) {
            log.error("Redis Hash删除字段失败: key={}", key, e);
            return 0L;
        }
    }
    
    /**
     * Hash判断字段是否存在
     * 
     * @param key 键
     * @param field 字段
     * @return 是否存在
     */
    public Boolean hExists(String key, String field) {
        try {
            return stringRedisTemplate.opsForHash().hasKey(key, field);
        } catch (Exception e) {
            log.error("Redis Hash判断字段存在失败: key={}, field={}", key, field, e);
            return false;
        }
    }
    
    // =============================List操作=============================
    
    /**
     * List从左侧推入元素
     * 
     * @param key 键
     * @param value 值
     * @return 列表长度
     */
    public Long lPush(String key, String value) {
        try {
            return stringRedisTemplate.opsForList().leftPush(key, value);
        } catch (Exception e) {
            log.error("Redis List左推失败: key={}", key, e);
            return 0L;
        }
    }
    
    /**
     * List从右侧推入元素
     * 
     * @param key 键
     * @param value 值
     * @return 列表长度
     */
    public Long rPush(String key, String value) {
        try {
            return stringRedisTemplate.opsForList().rightPush(key, value);
        } catch (Exception e) {
            log.error("Redis List右推失败: key={}", key, e);
            return 0L;
        }
    }
    
    /**
     * List从左侧弹出元素
     * 
     * @param key 键
     * @return 值
     */
    public String lPop(String key) {
        try {
            return stringRedisTemplate.opsForList().leftPop(key);
        } catch (Exception e) {
            log.error("Redis List左弹失败: key={}", key, e);
            return null;
        }
    }
    
    /**
     * List从右侧弹出元素
     * 
     * @param key 键
     * @return 值
     */
    public String rPop(String key) {
        try {
            return stringRedisTemplate.opsForList().rightPop(key);
        } catch (Exception e) {
            log.error("Redis List右弹失败: key={}", key, e);
            return null;
        }
    }
    
    /**
     * List获取指定范围的元素
     * 
     * @param key 键
     * @param start 开始位置
     * @param end 结束位置
     * @return 元素列表
     */
    public List<String> lRange(String key, long start, long end) {
        try {
            return stringRedisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            log.error("Redis List范围查询失败: key={}, start={}, end={}", key, start, end, e);
            return null;
        }
    }
    
    /**
     * List获取长度
     * 
     * @param key 键
     * @return 长度
     */
    public Long lSize(String key) {
        try {
            return stringRedisTemplate.opsForList().size(key);
        } catch (Exception e) {
            log.error("Redis List获取长度失败: key={}", key, e);
            return 0L;
        }
    }
    
    // =============================Set操作=============================
    
    /**
     * Set添加元素
     * 
     * @param key 键
     * @param values 值
     * @return 添加的数量
     */
    public Long sAdd(String key, String... values) {
        try {
            return stringRedisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            log.error("Redis Set添加元素失败: key={}", key, e);
            return 0L;
        }
    }
    
    /**
     * Set获取所有元素
     * 
     * @param key 键
     * @return 元素集合
     */
    public Set<String> sMembers(String key) {
        try {
            return stringRedisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            log.error("Redis Set获取所有元素失败: key={}", key, e);
            return null;
        }
    }
    
    /**
     * Set判断元素是否存在
     * 
     * @param key 键
     * @param value 值
     * @return 是否存在
     */
    public Boolean sIsMember(String key, String value) {
        try {
            return stringRedisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            log.error("Redis Set判断元素存在失败: key={}, value={}", key, value, e);
            return false;
        }
    }
    
    /**
     * Set删除元素
     * 
     * @param key 键
     * @param values 值
     * @return 删除的数量
     */
    public Long sRemove(String key, Object... values) {
        try {
            return stringRedisTemplate.opsForSet().remove(key, values);
        } catch (Exception e) {
            log.error("Redis Set删除元素失败: key={}", key, e);
            return 0L;
        }
    }
    
    /**
     * Set获取大小
     * 
     * @param key 键
     * @return 大小
     */
    public Long sSize(String key) {
        try {
            return stringRedisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            log.error("Redis Set获取大小失败: key={}", key, e);
            return 0L;
        }
    }
    
    // =============================ZSet操作=============================
    
    /**
     * ZSet添加元素
     * 
     * @param key 键
     * @param value 值
     * @param score 分数
     * @return 是否成功
     */
    public Boolean zAdd(String key, String value, double score) {
        try {
            return stringRedisTemplate.opsForZSet().add(key, value, score);
        } catch (Exception e) {
            log.error("Redis ZSet添加元素失败: key={}, value={}, score={}", key, value, score, e);
            return false;
        }
    }
    
    /**
     * ZSet获取指定范围的元素（按分数从小到大）
     * 
     * @param key 键
     * @param start 开始位置
     * @param end 结束位置
     * @return 元素集合
     */
    public Set<String> zRange(String key, long start, long end) {
        try {
            return stringRedisTemplate.opsForZSet().range(key, start, end);
        } catch (Exception e) {
            log.error("Redis ZSet范围查询失败: key={}, start={}, end={}", key, start, end, e);
            return null;
        }
    }
    
    /**
     * ZSet获取指定范围的元素（按分数从大到小）
     * 
     * @param key 键
     * @param start 开始位置
     * @param end 结束位置
     * @return 元素集合
     */
    public Set<String> zReverseRange(String key, long start, long end) {
        try {
            return stringRedisTemplate.opsForZSet().reverseRange(key, start, end);
        } catch (Exception e) {
            log.error("Redis ZSet反向范围查询失败: key={}, start={}, end={}", key, start, end, e);
            return null;
        }
    }
    
    /**
     * ZSet删除元素
     * 
     * @param key 键
     * @param values 值
     * @return 删除的数量
     */
    public Long zRemove(String key, Object... values) {
        try {
            return stringRedisTemplate.opsForZSet().remove(key, values);
        } catch (Exception e) {
            log.error("Redis ZSet删除元素失败: key={}", key, e);
            return 0L;
        }
    }
    
    /**
     * ZSet获取大小
     * 
     * @param key 键
     * @return 大小
     */
    public Long zSize(String key) {
        try {
            return stringRedisTemplate.opsForZSet().size(key);
        } catch (Exception e) {
            log.error("Redis ZSet获取大小失败: key={}", key, e);
            return 0L;
        }
    }
    
    // =============================通用操作=============================
    
    /**
     * 模糊查询键
     * 
     * @param pattern 匹配模式
     * @return 键集合
     */
    public Set<String> keys(String pattern) {
        try {
            return stringRedisTemplate.keys(pattern);
        } catch (Exception e) {
            log.error("Redis模糊查询键失败: pattern={}", pattern, e);
            return null;
        }
    }
    
    /**
     * 递增
     * 
     * @param key 键
     * @param delta 增量
     * @return 递增后的值
     */
    public Long increment(String key, long delta) {
        try {
            return stringRedisTemplate.opsForValue().increment(key, delta);
        } catch (Exception e) {
            log.error("Redis递增失败: key={}, delta={}", key, delta, e);
            return null;
        }
    }
    
    /**
     * 递减
     * 
     * @param key 键
     * @param delta 减量
     * @return 递减后的值
     */
    public Long decrement(String key, long delta) {
        try {
            return stringRedisTemplate.opsForValue().decrement(key, delta);
        } catch (Exception e) {
            log.error("Redis递减失败: key={}, delta={}", key, delta, e);
            return null;
        }
    }
}

